package com.pangu.core.engine.game;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.pangu.core.BMDataContext;
import com.pangu.core.cache.CacheHelper;
import com.pangu.core.engine.game.model.PlayerRecordJinhua;
import com.pangu.core.engine.game.state.GameEvent;
import com.pangu.core.nettyserver.client.NettyClients;
import com.pangu.core.nettyserver.handler.BeiMiClient;
import com.pangu.util.GameUtils;
import com.pangu.util.ProcessCard;
import com.pangu.util.rules.model.BetStatus;
import com.pangu.util.rules.model.Board;
import com.pangu.util.rules.model.JoinRoom;
import com.pangu.util.rules.model.Player;
import com.pangu.util.rules.model.TakeCards;
import com.pangu.util.rules.model.ZhajinhuaBoard;
import com.pangu.util.rules.model.ZhajinhuaTakeCards;
import com.pangu.web.model.GamePlayway;
import com.pangu.web.model.GameRoom;
import com.pangu.web.model.PlayUserClient;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * Description:重写炸金花游戏引擎
 *
 * @author abo
 * @date 2018年4月16日
 */
@Slf4j
@Service(value = "zhajinhuaEngine")
public class ZhajinhuaEngine {
	public void gameRequest(String userid, String playway, String room, String orgi, PlayUserClient userClient,
			BeiMiClient beiMiClient) {
		GameEvent gameEvent = gameRequest(userClient.getId(), beiMiClient.getPlayway(), beiMiClient,
				beiMiClient.getOrgi(), userClient);
		if (gameEvent != null) {
			/**
			 * 举手了，表示游戏可以开始了
			 */
			if (userClient != null) {
				userClient.setGamestatus(BMDataContext.GameStatusEnum.READY.toString());
			}

			/**
			 * 游戏状态 ， 玩家请求 游戏房间，活动房间状态后，发送事件给 StateMachine，由 StateMachine驱动 游戏状态 ，
			 * 此处只负责通知房间内的玩家 1、有新的玩家加入
			 * 2、给当前新加入的玩家发送房间中所有玩家信息（不包含隐私信息，根据业务需求，修改PlayUserClient的字段，剔除掉隐私信息后发送）
			 */
			ActionTaskUtils.sendEvent("joinroom", new JoinRoom(userClient, gameEvent.getIndex(),
					gameEvent.getGameRoom().getPlayers(), gameEvent.getGameRoom()), gameEvent.getGameRoom());
			/**
			 * 发送给单一玩家的消息
			 */
			ActionTaskUtils.sendPlayers(beiMiClient, gameEvent.getGameRoom());

			/**
			 * 当前是在游戏中还是 未开始
			 */
			Board board = (Board) CacheHelper.getBoardCacheBean().getCacheObject(gameEvent.getRoomid(),
					gameEvent.getOrgi());
			// if (board != null) {
			// Player currentPlayer = null;
			// for (Player player : board.getPlayers()) {
			// if (player.getPlayuser().equals(userClient.getId())) {
			// currentPlayer = player;
			// break;
			// }
			// }
			// if (currentPlayer != null) {
			//// boolean automic = false;
			//// if ((board.getLast() != null &&
			// board.getLast().getUserid().equals(currentPlayer.getPlayuser()))
			//// || (board.getLast() == null &&
			// board.getBanker().equals(currentPlayer.getPlayuser()))) {
			//// automic = true;
			//// }
			//// ActionTaskUtils.sendEvent("recovery",
			//// new RecoveryData(currentPlayer, board.getLasthands(),
			//// board.getNextplayer() != null ? board.getNextplayer().getNextplayer() :
			// null, 25,
			//// automic, board),
			//// gameEvent.getGameRoom());
			//// ActionTaskUtils.sendEvent("recovery",
			//// new RecoveryData(currentPlayer, board.getLasthands(),
			//// board.getNextplayer() != null ? board.getNextplayer().getNextplayer() :
			// null, 25,
			//// automic, board),
			//// gameEvent.getGameRoom());
			// ActionTaskUtils.sendEvent("recovery", board, gameEvent.getGameRoom());
			// }
			// } else {
			// 通知状态 此处应由状态机处理异步执行
			GameUtils.getGame(beiMiClient.getPlayway(), gameEvent.getOrgi()).change(gameEvent);
			// }
		}
	}

	/**
	 * 玩家房间选择， 新请求，游戏撮合， 如果当前玩家是断线重连， 或者是 退出后进入的，则第一步检查是否已在房间 如果已在房间，直接返回
	 * 
	 * @param userid
	 * @param orgi
	 * @return
	 */
	public GameEvent gameRequest(String userid, String playway, BeiMiClient beiMiClient, String orgi,
			PlayUserClient playUser) {
		GameEvent gameEvent = null;
		String roomid = (String) CacheHelper.getRoomMappingCacheBean().getCacheObject(userid, orgi);
		// 获取玩法
		GamePlayway gamePlayway = (GamePlayway) CacheHelper.getSystemCacheBean().getCacheObject(playway, orgi);
		boolean needtakequene = false;
		if (gamePlayway != null) {
			gameEvent = new GameEvent(gamePlayway.getPlayers(), gamePlayway.getCardsnum(), orgi);
			GameRoom gameRoom = null;
			if (!StringUtils.isBlank(roomid)
					&& CacheHelper.getGameRoomCacheBean().getCacheObject(roomid, orgi) != null) {//
				gameRoom = (GameRoom) CacheHelper.getGameRoomCacheBean().getCacheObject(roomid, orgi);
				// 直接加入到 系统缓存 （只有一个地方对GameRoom进行二次写入，避免分布式锁）
			} else {

				// 大厅游戏 ， 撮合游戏 , 发送异步消息，通知RingBuffer进行游戏撮合，撮合算法描述如下： 1、按照查找
				gameRoom = (GameRoom) CacheHelper.getQueneCache().poll(playway, orgi);
				// if (gameRoom != null) {
				// /**
				// * 修正获取gameroom获取的问题，因为删除房间的时候，为了不损失性能，没有将 队列里的房间信息删除，如果有玩家获取到这个垃圾信息
				// * 则立即进行重新获取房价，
				// */
				// while (CacheHelper.getGameRoomCacheBean().getCacheObject(gameRoom.getId(),
				// gameRoom.getOrgi()) == null) {
				// gameRoom = (GameRoom) CacheHelper.getQueneCache().poll(playway, orgi);
				// if (gameRoom == null) {
				// break;
				// }
				// }
				// }
				if (gameRoom == null) { // 无房间 ， 需要
					gameRoom = this.creatGameRoom(gamePlayway, userid, false, beiMiClient);
					// 设置游戏当前已经进行的局数
					gameRoom.setCurrentnum(0);
					// 更新缓存
					CacheHelper.getGameRoomCacheBean().put(gameRoom.getId(), gameRoom, orgi);
				} else {
					playUser.setPlayerindex(System.currentTimeMillis());// 从后往前坐，房主进入以后优先坐在 首位
					needtakequene = true;
				}
			}

			if (gameRoom != null) {
				// 如果当前房间到达了最大玩家数量，则不再加入到 撮合队列
				List<PlayUserClient> playerList = CacheHelper.getGamePlayerCacheBean().getCacheObject(gameRoom.getId(),
						gameRoom.getOrgi());
				// if (playerList.size() == 0) {
				gameEvent.setEvent(BeiMiGameEvent.ENTER.toString());// ？？？？？？
				// } else {
				// gameEvent.setEvent(BeiMiGameEvent.JOIN.toString());
				// }
				gameEvent.setGameRoom(gameRoom);
				gameEvent.setRoomid(gameRoom.getId());

				/**
				 * 无条件加入房间
				 */
				this.joinRoom(gameRoom, playUser, playerList);

				for (PlayUserClient temp : playerList) {
					if (temp.getId().equals(playUser.getId())) {
						gameEvent.setIndex(playerList.indexOf(temp));
						break;
					}
				}
				// 如果当前房间到达了最大玩家数量，则不再加入到 撮合队列
				if (playerList.size() < gamePlayway.getPlayers() && needtakequene == true) {
					CacheHelper.getQueneCache().put(gameRoom, orgi); // 未达到最大玩家数量，加入到游戏撮合 队列，继续撮合
				}
			}
		}
		return gameEvent;
	}

	/**
	 * 
	 * Description: 创建新房间 ，需要传入房间的玩法 ， 玩法定义在 系统运营后台，玩法创建后，放入系统缓存 ， 客户端进入房间的时候，传入
	 * 玩法ID参数
	 * 
	 * @author abo
	 * @date 2018年3月27日
	 * @param playway
	 * @param userid
	 * @param cardroom
	 * @param beiMiClient
	 * @return
	 */
	private GameRoom creatGameRoom(GamePlayway playway, String userid, boolean cardroom, BeiMiClient beiMiClient) {
		GameRoom gameRoom = new GameRoom();
		gameRoom.setCreatetime(new Date());
		// gameRoom.setRoomid(UKTools.getUUID());
		gameRoom.setRoomid(gameRoom.getId());
		gameRoom.setUpdatetime(new Date());
		// 玩法不等于空的时候
		if (playway != null) {
			gameRoom.setPlayway(playway.getId());
			gameRoom.setRoomtype(playway.getRoomtype());
			gameRoom.setPlayers(playway.getPlayers());
		}
		gameRoom.setPlayers(playway.getPlayers());
		gameRoom.setCardsnum(playway.getCardsnum());
		gameRoom.setCurpalyers(1);
		gameRoom.setCardroom(cardroom);
		gameRoom.setStatus(BeiMiGameEnum.CRERATED.toString());
		gameRoom.setCardsnum(playway.getCardsnum());
		gameRoom.setCurrentnum(0);
		gameRoom.setCreater(userid);
		gameRoom.setMaster(userid);
		gameRoom.setNumofgames(playway.getNumofgames()); // 无限制
		gameRoom.setOrgi(playway.getOrgi());

		gameRoom.setRoomtype(BMDataContext.ModelType.HALL.toString());

		CacheHelper.getQueneCache().put(gameRoom, playway.getOrgi()); // 未达到最大玩家数量，加入到游戏撮合 队列，继续撮合

		// DisruptorHandler.published(gameRoom, null,
		// BMDataContext.getContext().getBean(GameRoomRepository.class),
		// BMDataContext.UserDataEventType.SAVE.toString());

		return gameRoom;
	}

	/**
	 * 
	 * Description: 玩家加入房间
	 * 
	 * @author abo
	 * @date 2018年3月27日
	 * @param gameRoom
	 * @param playUser
	 * @param playerList
	 */
	public void joinRoom(GameRoom gameRoom, PlayUserClient playUser, List<PlayUserClient> playerList) {
		boolean inroom = false;
		for (PlayUserClient user : playerList) {
			if (user.getId().equals(playUser.getId())) {
				inroom = true;
				break;
			}
		}
		if (inroom == false) {
			playUser.setPlayerindex(System.currentTimeMillis());
			playUser.setGamestatus(BMDataContext.GameStatusEnum.PLAYING.toString());
			playUser.setPlayertype(BMDataContext.PlayerTypeEnum.NORMAL.toString());
			playUser.setRoomid(gameRoom.getId());
			// playUser.setRoomready(false);

			playerList.add(playUser);
			NettyClients.getInstance().joinRoom(playUser.getId(), gameRoom.getId());
			// 将用户加入到 room MultiCache
			CacheHelper.getGamePlayerCacheBean().put(playUser.getId(), playUser, playUser.getOrgi());
		}

		/**
		 * 不管状态如何，玩家一定会加入到这个房间
		 */
		CacheHelper.getRoomMappingCacheBean().put(playUser.getId(), gameRoom.getId(), playUser.getOrgi());
	}

	public TakeCards takeCardsRequest(String roomid, String playUserClient, String orgi, boolean auto,
			byte[] playCards) {
		TakeCards takeCards = null;
		GameRoom gameRoom = (GameRoom) CacheHelper.getGameRoomCacheBean().getCacheObject(roomid, orgi);
		if (gameRoom != null) {
			Board board = (Board) CacheHelper.getBoardCacheBean().getCacheObject(gameRoom.getId(), gameRoom.getOrgi());
			if (board != null) {
				// 找到玩家数据
				Player player = board.player(playUserClient);
				if (board.getNextplayer() != null && player.getPlayuser().equals(board.getNextplayer().getNextplayer())
						&& board.getNextplayer().isTakecard() == false) {
					takeCards = board.takeCardsRequest(gameRoom, board, player, orgi, auto, playCards);
				}
			}
		}
		return takeCards;
	}

	/**
	 * 
	 * Description: 执行下注，机器人和真人都调用此方法
	 * 
	 * @author abo
	 * @date 2018年4月27日
	 * @param userClient
	 * @param room
	 * @param betStatus
	 * @param map
	 * @return
	 */
	public BetStatus execBet(PlayUserClient userClient, GameRoom room, BetStatus betStatus, Map<String, String> map) {
		// 先去缓存取此轮有没有下注
		List<PlayerRecordJinhua> prList = (List<PlayerRecordJinhua>) CacheHelper.getGameRecordJinhuaCacheBean()
				.getCacheObject(userClient.getId(), userClient.getOrgi());
		if (prList == null) {
			prList = new ArrayList<PlayerRecordJinhua>();
		}
		double betCoin = Double.parseDouble(map.get("betcoin"));
		// 下注内容
		PlayerRecordJinhua pr = new PlayerRecordJinhua();
		pr.setRoomId(room.getId());
		pr.setOrgi("beimi");
		pr.setGameType(map.get("gametype"));
		pr.setGameTime(new Date());
		pr.setGameSubType("");
		pr.setPlayerId(userClient.getId());
		pr.setCoin(betCoin);
		pr.setIsBanker("1");// 0庄，1闲
		// 是否是机器人
		if (userClient.getPlayertype().equals(BMDataContext.PlayerTypeEnum.AI.toString())) {
			pr.setPlayerType("2");
		} else {
			pr.setPlayerType("1");
		}
		prList.add(pr);
		// 下注以后把下注内容放到缓存，等会去结算
		CacheHelper.getGameRecordJinhuaCacheBean().put(userClient.getId(), prList, userClient.getOrgi());
		betStatus.setBetstatus(1);
		betStatus.setMessage(
				"用户(" + userClient.getPlayertype() + ")：" + userClient.getNickname() + "下注：" + betCoin + "成功");
		betStatus.setBetCoin(betCoin);
		// 当前牌局信息
		ZhajinhuaBoard board = (ZhajinhuaBoard) CacheHelper.getBoardCacheBean().getCacheObject(room.getId(),
				room.getOrgi());
		// 下注后设置为最低标配
		board.setPosition((int) betCoin);
		CacheHelper.getBoardCacheBean().put(room.getId(), board, room.getOrgi());
		return betStatus;
	}

	/**
	 * 
	 * Description: 执行弃牌操作，机器人和真人都调用此方法
	 * 
	 * @author abo
	 * @date 2018年4月30日
	 * @param playUser
	 * @param roomId
	 * @param json
	 * @return
	 */
	public ZhajinhuaTakeCards execGiveup(PlayUserClient playUser, String roomId,
			ZhajinhuaTakeCards zhajinhuaTakeCards) {
		Board board = (Board) CacheHelper.getBoardCacheBean().getCacheObject(roomId, playUser.getOrgi());
		Player[] players = board.getPlayers();
		int currentPlayerNum = 0;
		// 获取当前请求看牌的player
		for (int i = 0; i < players.length; i++) {
			// 发起弃牌的人修改状态为弃牌
			if (players[i].getPlayuser().equals(playUser.getId())) {
				players[i].setEnd(true);
				zhajinhuaTakeCards.setLookCard(players[i].isDocatch());// 设置是否看牌
				zhajinhuaTakeCards.setGiveUpCard(true);// 设置弃牌
				continue;
			}
			// 当前未弃牌的人数或者未比牌淘汰者
			if (!players[i].isEnd() && !players[i].isHu()) {
				currentPlayerNum++;
			}
		}
		board.setPlayers(players);
		// 修改了弃牌人员信息，重新放回缓存
		CacheHelper.getBoardCacheBean().put(roomId, board, playUser.getOrgi());
		// json.put("currentPlayerNum", currentPlayerNum);
		// json.put("status", 1);
		// json.put("message", "请求成功");
		System.out.println("弃牌后还剩" + currentPlayerNum + "个玩家");
		// 未弃牌人数大于1游戏继续，未弃牌人数小于2前台发起结算
		if (currentPlayerNum > 1) {
			log.info("--------------------------------------" + playUser.getId() + "弃牌打断记时");
			// 激活注册机到下一步
			BMDataContext.getZhajinhuaEngine().takeCardsRequest(roomId, board.getNextplayer().getNextplayer(),
					playUser.getOrgi(), true, null);
		} else {
			log.info("--------------------------------------" + playUser.getId() + "弃牌人数不够开始结算");
			GameRoom gameRoom = (GameRoom) CacheHelper.getGameRoomCacheBean().getCacheObject(roomId,
					playUser.getOrgi());
			// 进入下一步结算 激活注册机到下一步
			GameUtils.getGame(gameRoom.getPlayway(), gameRoom.getOrgi()).change(gameRoom,
					BeiMiGameEvent.ALLCARDS.toString()); // 通知状态机
		}
		return zhajinhuaTakeCards;
	}

	/**
	 * 
	 * Description: 比牌，机器人和真人都调用此方法
	 * 
	 * @author abo
	 * @date 2018年4月30日
	 * @param playUser
	 * @param board
	 * @param json
	 * @param map
	 * @return
	 */
	public JSONObject execCompareCard(PlayUserClient playUser, Board board, JSONObject json, Map<String, String> map) {
		Player[] players = board.getPlayers();
		Player currentPlayer = board.player(playUser.getId());
		Player anotherPlayer = board.player(map.get("anotherPlayer"));
		String roomId = board.getRoom();
		// 比牌对象不为空
		if (anotherPlayer != null) {
			Map<String, Object> cardInfoMap = ProcessCard.compareRedBlackCards(currentPlayer.getCards(),
					anotherPlayer.getCards());
			String flag = cardInfoMap.get("flag").toString();
			// flag等于1打的时候发起比牌的方赢,修改输家状态isEnd为true
			String losePlayerId = null;
			if (flag.equals("1")) {
				losePlayerId = anotherPlayer.getPlayuser();
			} else {
				losePlayerId = currentPlayer.getPlayuser();
			}
			// 把修改后的玩家重新放入board
			for (int i = 0; i < players.length; i++) {
				if (players[i].getPlayuser().equals(losePlayerId)) {
					players[i].setHu(true);// 用此字段替代比牌淘汰
				}
			}
			board.setPlayers(players);
			CacheHelper.getBoardCacheBean().put(roomId, board, BMDataContext.SYSTEM_ORGI);
			// 判断存活玩家是否还有2家以上
			int currentPlayerNum = 0;
			for (int i = 0; i < players.length; i++) {
				// 当前存活人数，就是未弃牌，未被淘汰的人数
				if (!players[i].isEnd() && !players[i].isHu()) {
					currentPlayerNum++;
				}
			}
			log.info("比牌后还剩" + currentPlayerNum + "个玩家");
			json.put("cards", cardInfoMap);
			json.put("status", 1);
			json.put("message", "请求成功");
			json.put("currentPlayerNum", currentPlayerNum);
		} else {
			json.put("status", -1);
			json.put("message", "未获取到比牌玩家");
		}
		return json;
	}

	/**
	 * 结束 当前牌局，清除牌局相关的数据
	 * 
	 * @param orgi
	 * @return
	 */
	public void finished(String roomid, String orgi) {
		if (!StringUtils.isBlank(roomid)) {//
			// CacheHelper.getExpireCache().remove(roomid); 房间暂时永远不过期
			CacheHelper.getBoardCacheBean().delete(roomid, orgi);// 牌局过期
			CacheHelper.getGameRecordJinhuaCacheBean().delete(roomid, orgi);// 清除流水记录
		}
	}

	/**
	 * 解散房间 , 解散的时候，需要验证下，当前对象是否是房间的创建人
	 */
	public void dismissRoom(GameRoom gameRoom, String orgi) {
		// if(gameRoom.getMaster().equals(userid)){
		CacheHelper.getGamePlayerCacheBean().delete(gameRoom.getId(), orgi);
		List<PlayUserClient> players = CacheHelper.getGamePlayerCacheBean().getCacheObject(gameRoom.getId(), orgi);
		for (PlayUserClient player : players) {
			/**
			 * 解散房间的时候，只清理 AI
			 */
			// if(player.getPlayertype().equals(BMDataContext.PlayerTypeEnum.AI.toString())){
			CacheHelper.getGamePlayerCacheBean().delete(player.getId(), orgi);
			CacheHelper.getRoomMappingCacheBean().delete(player.getId(), orgi);
			// }
		}
		// 删掉缓存里面的房间
		// CacheHelper.getGameRoomCacheBean().delete(gameRoom.getId(), orgi);
		/**
		 * 先不删
		 */
		// UKTools.published(gameRoom, null,
		// BMDataContext.getContext().getBean(GameRoomRepository.class) ,
		// BMDataContext.UserDataEventType.DELETE.toString());
		// }
	}

	/**
	 * 
	 * Description: 判断当前房间里面还有没有在线的真人玩家。
	 * 
	 * @author abo
	 * @date 2018年6月2日
	 * @param roomId
	 * @return true还有在线的真人玩家，false没有在线的真人玩家
	 */
	public boolean isHaveNormalPlayerInGameRoom(String roomId) {
		// 获取房间里面的所有人
		List<PlayUserClient> plays = CacheHelper.getGamePlayerCacheBean().getCacheObject(roomId,
				BMDataContext.SYSTEM_ORGI);
		for (PlayUserClient playUserClient : plays) {
			// 通过房间里面的非ai去查询当前的状态，看看有没有托管和离开的玩家
			if (!playUserClient.getPlayertype().equals(BMDataContext.PlayerTypeEnum.AI.toString())) {
				PlayUserClient puc = (PlayUserClient) CacheHelper.getApiUserCacheBean()
						.getCacheObject(playUserClient.getId(), BMDataContext.SYSTEM_ORGI);
				String playType = puc.getPlayertype();
				if (playType.equals(BMDataContext.PlayerTypeEnum.NORMAL.toString())) {
					BeiMiClient client = NettyClients.getInstance().getClient(playUserClient.getId());
					// 检查玩家是否在线,后续可能还要加入判断心跳是否在线，目前只判断状态
					if (client != null && ActionTaskUtils.online(client.getUserid(), client.getOrgi())) {
						return true;
					}
				}
			}
		}
		return false;
	}
	
	/**
	 * 
	 * Description:  重新按照庄家进行排序,庄家排第一
	 * 
	 * @author abo
	 * @date 2018年6月6日  
	 * @param players
	 * @return
	 */
	public Player[] playerSortByBanker(Player[] players) {
		//调用Arrays.asList()生产的List的add、remove方法时报异常，这是由Arrays.asList() 返回的市Arrays的内部类ArrayList，
		//而不是java.util.ArrayList。Arrays的内部类ArrayList和java.util.ArrayList都是继承AbstractList，
		//remove、add等方法AbstractList中是默认throw UnsupportedOperationException而且不作任何操作。java.util.ArrayList重新了这些方法而Arrays的内部类ArrayList没有重新
		List<Player> list = new ArrayList<Player>(Arrays.asList(players));

		List<Player> newlist = new ArrayList<Player>();
		boolean bankerBackMark = false;
		// 先拿出庄家然后再排序
		for (int i = 0; i < list.size(); i++) {
			Player player = list.get(i);
			boolean banker = player.isBanker();
			if (banker || bankerBackMark) {
				newlist.add(player);
				bankerBackMark = true;
			}
		}
		//删除庄家和庄家以后的玩家
		list.removeAll(newlist);
		//把序列庄家以前的玩家加入到新集合
		newlist.addAll(list);
		//因为java中的强制类型转换是针对单个对象才有效果的，而List是多对象的集合，所以将整个List强制转换是不行的 
		return  newlist.toArray(new Player[0]);
	}
}
